#!/bin/bash
set -e

# Print information
echo "Building Kameo TypeScript SDK..."

# Create kameo-ts directory if it doesn't exist
mkdir -p kameo-ts/src

# Build the WASM package directly into kameo-ts
echo "Building with cargo..."
cargo build --target wasm32-unknown-unknown --release --features wasm

# Generate JavaScript bindings directly in kameo-ts directory
echo "Generating JavaScript bindings..."
if ! command -v wasm-bindgen >/dev/null 2>&1; then
    echo "Installing wasm-bindgen-cli..."
    cargo install wasm-bindgen-cli
fi

wasm-bindgen --target web --out-dir kameo-ts/src --out-name kameo-wasm target/wasm32-unknown-unknown/release/kameo.wasm

# Copy TypeScript definition file
echo "Copying TypeScript definitions..."
cp kameo.d.ts kameo-ts/src/

# Create the TypeScript wrapper files if they don't exist
if [ ! -f kameo-ts/src/index.ts ]; then
  echo "Creating TypeScript wrapper files..."
  cat > kameo-ts/src/index.ts << EOL
/**
 * Kameo TypeScript SDK
 * 
 * This is a TypeScript wrapper around the Kameo WASM bindings that provides
 * a more ergonomic API for TypeScript/JavaScript applications.
 */

import { KameoWasm as WasmKameo, JsActorRef } from './kameo-wasm';

// Re-export the KameoMessages namespace
export namespace KameoMessages {
  /**
   * Example counter actor messages
   */
  export namespace Counter {
    /**
     * Increment the counter
     */
    export interface Inc {
      amount: number;
    }

    /**
     * Get the current count
     */
    export interface GetCount {}
  }
}

/**
 * ActorRef class for interacting with Kameo actors
 */
export class ActorRef<TMessage = any, TResponse = any> {
  private actorRef: JsActorRef;

  constructor(actorRef: JsActorRef) {
    this.actorRef = actorRef;
  }

  /**
   * Send a message to the actor and get a reply
   * @param message The message to send to the actor
   * @returns A promise that resolves to the actor's reply
   */
  async ask(message: TMessage): Promise<TResponse> {
    return await this.actorRef.ask(message);
  }

  /**
   * Send a message to the actor without expecting a reply
   * @param message The message to send to the actor
   * @returns A promise that resolves when the message is sent
   */
  async tell(message: TMessage): Promise<void> {
    return await this.actorRef.tell(message);
  }
}

/**
 * Main Kameo class for interacting with the actor system
 */
export class Kameo {
  private wasmKameo: WasmKameo;
  
  /**
   * Initialize Kameo with WASM bindings
   */
  constructor() {
    this.wasmKameo = new WasmKameo();
  }

  /**
   * Spawn a new actor
   * @param actorType The type of actor to spawn
   * @param initData Initial data to configure the actor
   * @returns A reference to the spawned actor
   */
  spawn<TMessage = any, TResponse = any>(actorType: string, initData: any): ActorRef<TMessage, TResponse> {
    const nativeActorRef = this.wasmKameo.spawn(actorType, initData);
    return new ActorRef<TMessage, TResponse>(nativeActorRef);
  }

  /**
   * Register an actor with a lookup name
   * @param actorRef The actor reference to register
   * @param name The name to register the actor under
   * @returns A promise that resolves when the actor is registered
   */
  async registerActor<TMessage = any, TResponse = any>(
    actorRef: ActorRef<TMessage, TResponse>,
    name: string
  ): Promise<void> {
    // Unwrap the ActorRef to get the native JsActorRef
    const nativeActorRef = (actorRef as any).actorRef;
    await this.wasmKameo.register_actor(nativeActorRef, name);
  }

  /**
   * Look up an actor by name
   * @param actorType The type of actor to look up
   * @param name The name of the actor to look up
   * @returns A promise that resolves to the actor reference, if found
   */
  async lookupActor<TMessage = any, TResponse = any>(
    actorType: string,
    name: string
  ): Promise<ActorRef<TMessage, TResponse>> {
    const nativeActorRef = await this.wasmKameo.lookup_actor(actorType, name);
    return new ActorRef<TMessage, TResponse>(nativeActorRef);
  }
}

// Export a default Kameo instance for convenience
export const kameo = new Kameo();
export default kameo;
EOL
fi

# Create test directory and test files if they don't exist
mkdir -p kameo-ts/test
if [ ! -f kameo-ts/test/kameo.test.ts ]; then
  echo "Creating test files..."
  cat > kameo-ts/test/kameo.test.ts << EOL
import { test, expect, mock, describe } from "bun:test";
import { Kameo, ActorRef, KameoMessages } from "../src/index";

describe("Kameo SDK", () => {
  test("Kameo class should be defined", () => {
    const kameo = new Kameo();
    expect(kameo).toBeDefined();
  });

  test("ActorRef methods should be defined", () => {
    // Mock the JsActorRef for testing
    const mockJsActorRef = {
      ask: mock(() => Promise.resolve(5)),
      tell: mock(() => Promise.resolve())
    };

    // @ts-ignore - bypass private constructor
    const actorRef = new ActorRef(mockJsActorRef);
    expect(actorRef).toBeDefined();
    expect(typeof actorRef.ask).toBe("function");
    expect(typeof actorRef.tell).toBe("function");
  });

  test("ActorRef.ask() should call the underlying JsActorRef.ask()", async () => {
    const mockJsActorRef = {
      ask: mock(() => Promise.resolve(5)),
      tell: mock(() => Promise.resolve())
    };

    // @ts-ignore - bypass private constructor
    const actorRef = new ActorRef(mockJsActorRef);
    const result = await actorRef.ask({ amount: 5 });
    
    expect(result).toBe(5);
    expect(mockJsActorRef.ask).toHaveBeenCalledTimes(1);
    expect(mockJsActorRef.ask).toHaveBeenCalledWith({ amount: 5 });
  });

  test("ActorRef.tell() should call the underlying JsActorRef.tell()", async () => {
    const mockJsActorRef = {
      ask: mock(() => Promise.resolve(5)),
      tell: mock(() => Promise.resolve())
    };

    // @ts-ignore - bypass private constructor
    const actorRef = new ActorRef(mockJsActorRef);
    await actorRef.tell({ action: "increment" });
    
    expect(mockJsActorRef.tell).toHaveBeenCalledTimes(1);
    expect(mockJsActorRef.tell).toHaveBeenCalledWith({ action: "increment" });
  });

  test("KameoMessages namespace should be defined", () => {
    expect(KameoMessages).toBeDefined();
    expect(KameoMessages.Counter).toBeDefined();
  });
});
EOL

  cat > kameo-ts/test/setup.ts << EOL
// 全局测试设置
console.log("Setting up tests for Kameo SDK...");

// 模拟WebAssembly环境（如果需要）
// 这里我们可以模拟WASM相关功能，但目前测试使用mock来避免实际调用WASM
EOL
fi

# Create bunfig.toml if it doesn't exist
if [ ! -f kameo-ts/bunfig.toml ]; then
  echo "Creating Bun configuration..."
  cat > kameo-ts/bunfig.toml << EOL
[test]
# 配置测试环境
preload = ["./test/setup.ts"]

[build]
target = "browser"
outdir = "./dist"
minify = true

[install]
exact = true
EOL
fi

# Create or update tsconfig.json file
if [ ! -f kameo-ts/tsconfig.json ]; then
  echo "Creating tsconfig.json..."
  cat > kameo-ts/tsconfig.json << EOL
{
  "compilerOptions": {
    "target": "es2020",
    "module": "esnext",
    "moduleResolution": "bundler",
    "declaration": true,
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "types": ["bun-types"]
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.test.ts"]
}
EOL
fi

# Create or update package.json file
if [ ! -f kameo-ts/package.json ]; then
  echo "Creating package.json..."
  cat > kameo-ts/package.json << EOL
{
  "name": "kameo-ts",
  "version": "0.1.0",
  "description": "TypeScript SDK for the Kameo actor system",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist/**/*",
    "src/kameo-wasm.js",
    "src/kameo-wasm.d.ts",
    "src/kameo-wasm_bg.wasm",
    "src/kameo-wasm_bg.wasm.d.ts",
    "src/kameo.d.ts"
  ],
  "scripts": {
    "build": "bun build ./src/index.ts --outdir ./dist --format esm",
    "build:types": "tsc --emitDeclarationOnly",
    "clean": "rm -rf dist",
    "prepare": "bun run clean && bun run build && bun run build:types",
    "test": "bun test",
    "test:watch": "bun test --watch",
    "prepublishOnly": "bun run prepare"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "bun-types": "latest"
  },
  "keywords": [
    "actor",
    "typescript",
    "kameo"
  ],
  "author": "",
  "license": "MIT OR Apache-2.0",
  "repository": {
    "type": "git",
    "url": "https://github.com/tqwewe/kameo"
  }
}
EOL
fi

# Create or update README.md file
if [ ! -f kameo-ts/README.md ]; then
  echo "Creating README.md..."
  cat > kameo-ts/README.md << EOL
# Kameo TypeScript SDK

A TypeScript SDK for the [Kameo](https://github.com/tqwewe/kameo) actor system.

## Installation

\`\`\`bash
npm install kameo-ts
\`\`\`

## Usage

\`\`\`typescript
import { Kameo, KameoMessages } from 'kameo-ts';

// Create a new instance of the Kameo API
const kameo = new Kameo();

// Define message types for better type checking
interface CounterInc { amount: number; }
interface CounterGetCount {}
type CounterMessage = CounterInc | CounterGetCount;
type CounterResponse = number;

// Spawn a counter actor with type information
const counterActor = kameo.spawn<CounterMessage, CounterResponse>('Counter', { count: 0 });

// Send a message to the actor with full type checking
counterActor.ask({ amount: 5 })
  .then(count => console.log('New count:', count));

// Register the actor for lookup
kameo.registerActor(counterActor, 'my-counter')
  .then(() => console.log('Actor registered'));

// Look up an actor with type information
kameo.lookupActor<CounterMessage, CounterResponse>('Counter', 'my-counter')
  .then(actor => {
    console.log('Actor found');
    return actor.ask({ amount: 10 });
  })
  .then(newCount => console.log('New count:', newCount));
\`\`\`

## Development

### Building the SDK
\`\`\`bash
bun run build
\`\`\`

### Running Tests
\`\`\`bash
bun test
\`\`\`

## TypeScript SDK Features

- Fully typed API with generics for message and response types
- Simplified actor reference API
- Better error handling
- Default exported instance for quick setup

## License

MIT OR Apache-2.0
EOL
fi

# Build the TypeScript SDK
echo "Building the TypeScript SDK..."
cd kameo-ts
if ! command -v bun >/dev/null 2>&1; then
    echo "Bun is required to build the TypeScript SDK. Install with: curl -fsSL https://bun.sh/install | bash"
    exit 1
fi
bun install
bun run build
bun test

cd ..

echo "TypeScript SDK built successfully in the 'kameo-ts' directory!"

# Function to handle publishing
publish_to_npm() {
  echo "Publishing to npm..."
  cd kameo-ts
  
  # Check if user is logged in to npm
  if ! npm whoami &> /dev/null; then
    echo "You are not logged in to npm. Please login first."
    npm login
  fi
  
  # Prompt for version bump
  echo "Current version is $(npm version | grep kameo-ts | cut -d"'" -f4)"
  echo "Would you like to bump the version? (y/n)"
  read -r answer
  if [ "$answer" = "y" ]; then
    echo "Select version bump type:"
    echo "1) patch (0.1.0 -> 0.1.1)"
    echo "2) minor (0.1.0 -> 0.2.0)"
    echo "3) major (0.1.0 -> 1.0.0)"
    read -r bump_type
    
    case $bump_type in
      1) npm version patch ;;
      2) npm version minor ;;
      3) npm version major ;;
      *) echo "Invalid option. Skipping version bump." ;;
    esac
  fi
  
  # Confirm publishing
  echo "Are you sure you want to publish? (y/n)"
  read -r confirm
  if [ "$confirm" = "y" ]; then
    npm publish
    echo "kameo-ts has been published to npm!"
  else
    echo "Publishing cancelled."
  fi
  
  cd ..
}

# Check if --publish flag is set
if [ "$1" = "--publish" ]; then
  publish_to_npm
else
  echo "To publish to npm, run './scripts/build-wasm.sh --publish'"
fi